/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package TeoriaDeColas;

import cern.jet.random.Poisson;
import cern.jet.random.engine.DRand;
import cern.jet.random.engine.RandomEngine;
import java.util.ArrayList;
import java.util.concurrent.Semaphore;
import javax.swing.JPanel;


/**
 *
 * @author Karadak
 */
public class Engine implements Runnable
{
    // variables de teoria de colas
    private int k_cantidadServidores;
    private int m_tamañoCola;
    private double lambda_tasaLlegada;
    
    private ArrayList<Servidor> servidores;
    public ArrayList<Cliente> cola;
    private ArrayList<Double> tasas;
    
    private Thread producer;        // genera las entradas al sistema, lo alimenta con clientes
    private boolean esInfinito;     // bandera de cola infinita
    private boolean activo;         //  esta activo 
    private RandomEngine rndom;     // genera numeros aleatorios
    private Poisson poisson;        // distribucion de llegada de clientes
    
    public static int clientesLlegan = 0;   // contador de clientes
    
    
    /**
     * paneles de animacion
     */
    private javax.swing.JPanel linea_entrada;
    private javax.swing.JPanel linea_servicio;
    private javax.swing.JPanel stackServidores;
    private javax.swing.JPanel lina_salida;
   
    
    /**
     * modelo con limite de capacidad en la cola M
     * @param k
     * @param M
     * @param mu
     * @param lambda 
     */    // finito
    public Engine(int k, int M, ArrayList mu, double lambda)
    {
        this.k_cantidadServidores = k;
        this.m_tamañoCola = M;
        this.tasas = mu;
        this.lambda_tasaLlegada = lambda;
        Variables_Globales.longitudFila = new Semaphore(m_tamañoCola, true);
        Variables_Globales.clientela = new ArrayList();
        
        cola = new ArrayList();
        servidores = new ArrayList();
        Engine.clientesLlegan = 0;
    }
    
    /**
     * Modelo con capacidad en cola infinita
     * @param k
     * @param mu
     * @param lambda 
     */   // infinito
    public Engine(int k, ArrayList mu, double lambda)
    {
        esInfinito = true;
        this.k_cantidadServidores = k;        
        this.tasas = mu;
        this.lambda_tasaLlegada = lambda;
        Variables_Globales.longitudFila = new Semaphore(m_tamañoCola, true);
        Variables_Globales.clientela = new ArrayList();
        
        cola = new ArrayList();
        servidores = new ArrayList();
         Engine.clientesLlegan = 0;
    }
    
    /**
     * inicia todas las variables y lo prepara para iniciar actividad
     */
    public void iniciarSistema()
    {
        Variables_Globales.hilos = new ArrayList();             // inicializamos el array de hilos globales
        this.activo = true;                                     // bandera de actividad
        this.rndom = new DRand();                               // generamos numereos aleatorios
//        poisson = new Poisson(lambda_tasaLlegada, rndom);   // distribucion de llegada
        poisson = new Poisson(1/lambda_tasaLlegada, rndom);   // distribucion de llegada
        Variables_Globales.Servicio = new Semaphore(k_cantidadServidores, true); // linea de servidores que atenderan
        Variables_Globales.hay_cola = new Semaphore(0, true);   // controlara como va ir la cola de espera
        Variables_Globales.mutex = new Semaphore(1, true);      // verificamos cuale esta ocupado o no: Mutual exclusion
        
        activarServidores();                                    // iniciamos los servidores de atencion
        Variables_Globales.hilos.add(producer);                 // agregamos el servidor de entrada de clientes
        producer = new Thread(this);                            // inicializamos el hilo principal
        producer.start();                                       // levantamos el hilo       
    }
    
    private void activarServidores() 
    {
        // creamos el numero de servidores indicados por el usuario, la misma tasa de atencion para todos
        Servidor aux;
        stackServidores.setBorder(new javax.swing.border.MatteBorder(null));
        
        for (int i = 0; i < k_cantidadServidores; i++)
        {
            aux = new Servidor(tasas.get(i), getCola());
            this.stackServidores.add(aux);                      // agregamos un servidor al panel de servidores     
            
            aux.setAlServicio(this.linea_servicio);             // agregamos label al camino de linea de servicio
            aux.setSalida(this.lina_salida);                    // agreagamos label al panel de salida
            
            
            getServidores().add(aux);       // agregamos servidor a la lista de servidores
        }
    }

    /**
     * controlamos el hilo de ejecucion del motor de simulacion
     */
    @Override
    public void run() 
    {        
        
        
        try
        {
            double tiempo;
            double contador = 0;
            Cliente clon_cliente;
            
            while (activo)
            {               
                try
                {                 
                    if (!esInfinito)                             // verificamos si es infinita la cola o no
                    {
                        Variables_Globales.longitudFila.acquire();  // sistema con limite de cola                        
                    }                   
                    
                    tiempo = this.poisson.nextDouble();         // obtenemos un numero de la distribucion == tiempo
                  
                    while (contador < tiempo) 
                    {                        
                        
                        contador++;                             // aumentamos el contador
                        Thread.sleep(Variables_Globales.velocidad_simulador);        // velocidad de simulacion
                        
                        while(TeoriaDeColas.Variables_Globales.pausar_simulador)    // si esta en pausa el simulador
                        {
                            Thread.sleep(100);                 // pausa
                            System.out.println("simulador pausado");
                        }
                    }
                    
                    Variables_Globales.mutex.acquire();         // switch para procesos, solo este hilo puede procesar
                    clon_cliente = new Cliente();               // creamos un nuevo cliente
                    
                    linea_entrada.add(clon_cliente);            // cliente agregado a la primer linea
                    
                    getCola().add(clon_cliente);               // el cliente va a la cola
                    
                    Variables_Globales.mutex.release();         // liberamos el proceso
                    Variables_Globales.hay_cola.release();      // liberamos elemento de la cola
                    
                    
                    Engine.clientesLlegan++;
                    System.out.println(Engine.clientesLlegan+" : Cliente llega al sistema en --> "+tiempo + " segundos");
                    contador = 0;                               // tiempo a cero, termino el cliente su transaccion
                    
                    while(Variables_Globales.pausar_simulador)  // esta pausado el sistema?
                    {
                        Thread.sleep(100);
                    }
                }
                catch (InterruptedException errr) 
                {
                    System.err.println("error en contadores de sistema activo "+ errr);
                }
            }
        }
        catch(Exception er)
        {
            System.err.println(" error en run() de Engine "+er);
        }
        
    }

    public ArrayList<Cliente> getCola() {
        return cola;
    }

    public ArrayList<Servidor> getServidores() {
        return servidores;
    }
    
    public void killThread()
    {
        activo = false;
        Variables_Globales.velocidad_simulador=1000000;
    }

    public void setLinea_entrada(JPanel linea_entrada) {
        this.linea_entrada = linea_entrada;
    }

    public void setLinea_servicio(JPanel linea_servicio) {
        this.linea_servicio = linea_servicio;
    }

    public void setStackServidores(JPanel stackServidores) {
        this.stackServidores = stackServidores;
    }

    public void setLina_salida(JPanel lina_salida) {
        this.lina_salida = lina_salida;
        lina_salida.setBorder(new javax.swing.border.MatteBorder(null));
        lina_salida.updateUI();
        lina_salida.show();
        
    }

    
    
    
}
